package folexpr.compiler.visitors.i_removebracket;

import folexpr.compiler.CompilerConstants;
import folexpr.compiler.CompilerUtils;
import folexpr.compiler.visitors.CompilerVisitor;
import folexpr.parser.FOLParser;
import folexpr.parser.ParseException;
import folexpr.parser.syntaxtree.*;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.StringReader;
import java.util.*;

/**
 * 
 * Simboli non terminali maggiormente interessati: 
 * 
 */

public class RemoveUnneededBracketVisitor extends CompilerVisitor<RemoveUnneededBracketVisitParameter>
{
	
	public RemoveUnneededBracketVisitor()	
	{
		super(CompilerConstants.REMOVE_BRACKET);
		
		// override
		this.debug = CompilerConstants.LOG_REMOVE_BRACKET_VISITOR;
	}

	public RemoveUnneededBracketVisitor(int startAtLevel)	
	{
		super(CompilerConstants.REMOVE_BRACKET);
		this.debug = CompilerConstants.LOG_REMOVE_BRACKET_VISITOR;
		this.level = startAtLevel;
	}
	
	/////////////////////////////////////////////////////////////////////////////////////

	// only dumping - ok
	@Override public RemoveUnneededBracketVisitResult visit(one_line n, RemoveUnneededBracketVisitParameter argu)
	{	
		this.level++;
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"VISIT(" + n.getClass().getName() + ") : INIZIO");
		RemoveUnneededBracketVisitResult outerResult = new RemoveUnneededBracketVisitResult();

		outerResult = (RemoveUnneededBracketVisitResult)n.f0.accept(this, new RemoveUnneededBracketVisitParameter());
		
		String negReducedExpression = outerResult.getResultExpr();
		String line_terminator = n.f1.accept(this,new RemoveUnneededBracketVisitParameter()).getResultExpr();
		outerResult.setResultExpr(negReducedExpression+line_terminator);
		
		////////////////////////////////////////////////////////////////////////////////////////////////
		
		try
		{
			if (debug) System.out.println("################## STEP "+ CompilerConstants.REMOVE_BRACKET +": RESULT EXPRESSION = " + outerResult.getResultExpr());
			StringReader inputStream = new StringReader(outerResult.getResultExpr());
			FOLParser.ReInit(inputStream);
			
			PrintStream nps=null;
			try { nps = new PrintStream(new FileOutputStream("NUL:")); } 
			catch (FileNotFoundException e) { e.printStackTrace(); }
			PrintStream out = System.out;
			
			if (!debug_syntaxtree_Reconversion) System.setOut( nps );	
			outerResult.setIntermediate_syntaxtree(FOLParser.one_line());	
			if (!debug_syntaxtree_Reconversion) System.setOut( out );
			
		} 
		catch (ParseException e) {
			if (debug) System.out.println("Errore durante la riconversione da stringa in albero sintattico dopo lo step " + CompilerConstants.REMOVE_BRACKET);
			e.printStackTrace();
		}

		if (debug) System.out.println(CompilerUtils.indent(level,8)+"risultato:" + outerResult.toString());
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"VISIT(" + n.getClass().getName() +") : FINE");
		this.level--;
		return outerResult;
	}

	// prima visita la parte destra, ... - ok
	@Override public RemoveUnneededBracketVisitResult visit(fbf n, RemoveUnneededBracketVisitParameter argu)
	{
		this.level++;
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"VISIT(" + n.getClass().getName() + ") : INIZIO");
		RemoveUnneededBracketVisitResult result = new RemoveUnneededBracketVisitResult();
		
		RemoveUnneededBracketVisitResult f0_result = null;
		
		// genero un nuovo parametro, inizializzo la "macchina a stati"
		RemoveUnneededBracketVisitResult f1_result = 
			(RemoveUnneededBracketVisitResult)n.f1.accept(this,new RemoveUnneededBracketVisitParameter(true,false,false,false,false));
		
		if ((f1_result.getResultExpr()==null) || (f1_result.getResultExpr().equals("")))
		{
			// implicazione degenere 
			//f0_result = n.f0.accept(this,new RemoveUnneededBracketVisitParameter(false,true,false,false,false));
			f0_result = (RemoveUnneededBracketVisitResult)n.f0.accept(this,argu);
			
			result.setContainsImplic(f0_result.containsImplic());
			result.setContainsDisg(f0_result.containsDisg());
			result.setContainsCong(f0_result.containsCong());
			result.setContainsUnary(f0_result.containsUnary());
			result.setContainsTerm(f0_result.containsTerm());
		}
		else
		{
			f0_result = (RemoveUnneededBracketVisitResult)n.f0.accept(this,new RemoveUnneededBracketVisitParameter(true,false,false,false,false));
			
			result.setContainsImplic(true);
			result.setContainsDisg(false);
			result.setContainsCong(false);
			result.setContainsUnary(false);
			result.setContainsTerm(false);
		}
			
		// poi alla fine faccio il dump della stringa
		result.addResultString(f0_result.getResultExpr());
		result.addResultString(f1_result.getResultExpr());
		
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"risultato:" + result.toString() + ". argomento:" + argu.toString());
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"VISIT(" + n.getClass().getName() + ") : FINE");
		this.level--;
		return result;
	}
	
	// prima visita la parte destra, ... - ok
	@Override public RemoveUnneededBracketVisitResult visit(disgiunzione n, RemoveUnneededBracketVisitParameter argu)
	{
		this.level++;
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"VISIT(" + n.getClass().getName() + ") : INIZIO");
		RemoveUnneededBracketVisitResult result = new RemoveUnneededBracketVisitResult();
		
		RemoveUnneededBracketVisitResult f0_result = null;
		
		// genero un nuovo parametro, inizializzo la "macchina a stati"
		RemoveUnneededBracketVisitResult f1_result = 
			(RemoveUnneededBracketVisitResult)n.f1.accept(this,new RemoveUnneededBracketVisitParameter(false,true,false,false,false));
		
		if ((f1_result.getResultExpr()==null) || (f1_result.getResultExpr().equals("")))
		{
			// disgiunzione degenere 
			//f0_result = n.f0.accept(this,new RemoveUnneededBracketVisitParameter(false,false,true,false,false));
			f0_result = (RemoveUnneededBracketVisitResult)n.f0.accept(this,argu);
			
			result.setContainsImplic(f0_result.containsImplic());
			result.setContainsDisg(f0_result.containsDisg());
			result.setContainsCong(f0_result.containsCong());
			result.setContainsUnary(f0_result.containsUnary());
			result.setContainsTerm(f0_result.containsTerm());
		}
		else
		{
			f0_result = (RemoveUnneededBracketVisitResult)n.f0.accept(this,new RemoveUnneededBracketVisitParameter(false,true,false,false,false));
			
			result.setContainsImplic(false);
			result.setContainsDisg(true);
			result.setContainsCong(false);
			result.setContainsUnary(false);
			result.setContainsTerm(false);
		}
		
		// poi alla fine faccio il dump della stringa
		result.addResultString(f0_result.getResultExpr());
		result.addResultString(f1_result.getResultExpr());
		
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"risultato:" + result.toString() + ". argomento:" + argu.toString());
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"VISIT(" + n.getClass().getName() + ") : FINE");
		this.level--;
		return result;
	}

	// prima visita la parte destra, ... - ok
	@Override public RemoveUnneededBracketVisitResult visit(congiunzione n, RemoveUnneededBracketVisitParameter argu)
	{
		this.level++;
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"VISIT(" + n.getClass().getName() + ") : INIZIO");
		RemoveUnneededBracketVisitResult result = new RemoveUnneededBracketVisitResult();
		
		RemoveUnneededBracketVisitResult f0_result = null;
		
		// genero un nuovo parametro, ri-inizializzo la "macchina a stati"
		RemoveUnneededBracketVisitResult f1_result = 
			(RemoveUnneededBracketVisitResult)n.f1.accept(this,new RemoveUnneededBracketVisitParameter(false,false,true,false,false));
		
		if ((f1_result.getResultExpr()==null) || (f1_result.getResultExpr().equals("")))
		{
			// congiunzione degenere 
			//f0_result = n.f0.accept(this,new RemoveUnneededBracketVisitParameter(false,false,false,false,true));
			f0_result = (RemoveUnneededBracketVisitResult)n.f0.accept(this,argu);
			
			result.setContainsImplic(f0_result.containsImplic());
			result.setContainsDisg(f0_result.containsDisg());
			result.setContainsCong(f0_result.containsCong());
			result.setContainsUnary(f0_result.containsUnary());
			result.setContainsTerm(f0_result.containsTerm());
		}
		else
		{
			f0_result = (RemoveUnneededBracketVisitResult)n.f0.accept(this,new RemoveUnneededBracketVisitParameter(false,false,true,false,false));
			
			result.setContainsImplic(false);
			result.setContainsDisg(false);
			result.setContainsCong(true);
			result.setContainsUnary(false);
			result.setContainsTerm(false);
		}

		// poi alla fine faccio il dump della stringa
		result.addResultString(f0_result.getResultExpr());
		result.addResultString(f1_result.getResultExpr());
		
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"risultato:" + result.toString() + ". argomento:" + argu.toString());
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"VISIT(" + n.getClass().getName() + ") : FINE");
		this.level--;
		return result;
	}
	
	// setta il parametro a (F,F,F,F,T) poi se c'� un unario al suo interno riporter� argu a (F,F,F,T,F) - ok
	@Override public RemoveUnneededBracketVisitResult visit(letterale n, RemoveUnneededBracketVisitParameter argu)
	{
		this.level++;
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"VISIT(" + n.getClass().getName() + ") : INIZIO");
		RemoveUnneededBracketVisitResult result = new RemoveUnneededBracketVisitResult();
		
		if (argu.isInsideUnaryOp())
			result = (RemoveUnneededBracketVisitResult)n.f0.accept(this,new RemoveUnneededBracketVisitParameter(false,false,false,true,false));
		else
		{
			if (argu.isInsideImplic() || argu.isInsideDisg() || argu.isInsideCong()) 
				result = (RemoveUnneededBracketVisitResult)n.f0.accept(this,argu);
			else  
				result = (RemoveUnneededBracketVisitResult)n.f0.accept(this,new RemoveUnneededBracketVisitParameter(false,false,false,false,true));
		}
		
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"risultato:" + result.toString() + ". argomento:" + argu.toString());
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"VISIT(" + n.getClass().getName() + ") : FINE");
		this.level--;
		return result;
	}
	
	// setta il parametro a (F,F,F,T,F) - ok
	@Override public RemoveUnneededBracketVisitResult visit(quantificazione n, RemoveUnneededBracketVisitParameter argu)
	{
		this.level++;
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"VISIT(" + n.getClass().getName() + ") : INIZIO");
		RemoveUnneededBracketVisitResult result = new RemoveUnneededBracketVisitResult();

		RemoveUnneededBracketVisitResult f0_result = (RemoveUnneededBracketVisitResult)n.f0.accept(this,argu);
		RemoveUnneededBracketVisitResult f1_result = (RemoveUnneededBracketVisitResult)n.f1.accept(this,argu);
		RemoveUnneededBracketVisitResult f2_result = 
				(RemoveUnneededBracketVisitResult)n.f2.accept(this,new RemoveUnneededBracketVisitParameter(false,false,false,true,false));
		
		result.setResultExpr(f0_result.getResultExpr() + f1_result.getResultExpr() + f2_result.getResultExpr());
		
		result.setContainsImplic(false);
		result.setContainsDisg(false);
		result.setContainsCong(false);
		result.setContainsUnary(true);
		result.setContainsTerm(false);	
				
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"risultato:" + result.toString() + ". argomento:" + argu.toString());
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"VISIT(" + n.getClass().getName() + ") : FINE");
		this.level--;
		return result;
	}

	// setta il parametro a (F,F,F,T,F) - ok
	@Override public RemoveUnneededBracketVisitResult visit(negazione n, RemoveUnneededBracketVisitParameter argu)
	{
		this.level++;
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"VISIT(" + n.getClass().getName() + ") : INIZIO");
		RemoveUnneededBracketVisitResult result = new RemoveUnneededBracketVisitResult();
		
		RemoveUnneededBracketVisitResult f0_result = (RemoveUnneededBracketVisitResult)n.f0.accept(this,argu);
		RemoveUnneededBracketVisitResult f1_result = 
				(RemoveUnneededBracketVisitResult)n.f1.accept(this,new RemoveUnneededBracketVisitParameter(false,false,false,true,false));
		
		result.addResultString(f0_result.getResultExpr());
		result.addResultString(f1_result.getResultExpr());

		result.setContainsImplic(false);
		result.setContainsDisg(false);
		result.setContainsCong(false);
		result.setContainsUnary(true);
		result.setContainsTerm(false);	
		
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"risultato:" + result.toString() + ". argomento:" + argu.toString());
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"VISIT(" + n.getClass().getName() + ") : FINE");
		this.level--;
		return result;
	}
	
	// setta il suo parametro - ok - forse pu� anche non settare argu, tanto gli arriva giusto da letterale se non ci sono op. unari
	@Override public RemoveUnneededBracketVisitResult visit(termine n, RemoveUnneededBracketVisitParameter argu)
	{
		this.level++;
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"VISIT(" + n.getClass().getName() + ") : INIZIO");
		RemoveUnneededBracketVisitResult result = new RemoveUnneededBracketVisitResult();
	
		result.addResultString(n.f0.accept(this,new RemoveUnneededBracketVisitParameter(false,false,false,false,true)).getResultExpr());
		
		result.setContainsImplic(false);
		result.setContainsDisg(false);
		result.setContainsCong(false);
		result.setContainsUnary(false);
		result.setContainsTerm(true);	
		
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"risultato:" + result.toString() + ". argomento:" + argu.toString());
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"VISIT(" + n.getClass().getName() + ") : FINE");
		this.level--;
		return result;
	}
	
	// "implementazione della macchina a stati2 - ok
	@Override public RemoveUnneededBracketVisitResult visit(NodeSequence n, RemoveUnneededBracketVisitParameter argu)
	{
		this.level++;
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"VISIT(" + n.getClass().getName() + ") : INIZIO");
		RemoveUnneededBracketVisitResult result = new RemoveUnneededBracketVisitResult();
		
		RemoveUnneededBracketVisitResult temp_result = new RemoveUnneededBracketVisitResult();
		Vector<RemoveUnneededBracketVisitResult> nodes = new Vector<RemoveUnneededBracketVisitResult>();
		
		int _count = 0;		
		for (Enumeration<Node> e = n.elements(); e.hasMoreElements();)
		{
			if (!argu.isInsideImplic() && !argu.isInsideDisg() && !argu.isInsideCong() )
				temp_result = (RemoveUnneededBracketVisitResult)e.nextElement().accept(this,new RemoveUnneededBracketVisitParameter());
			else
				temp_result = (RemoveUnneededBracketVisitResult)e.nextElement().accept(this,argu);

			//	temp_result = (RemoveUnneededBracketVisitResult)e.nextElement().accept(this,argu);
			
			nodes.add(temp_result);
			result.addResultString(temp_result.getResultExpr());
			_count++;
		}
		
		
		if (_count==2) // parte destra di cong/disg/fbf : "AND dx_operand" oppure "OR dx_operand" oppure "<=> dx_operand" o anche argomento di un termine ", arg"
		{
			result.setResultExpr(nodes.get(0).getResultExpr() + nodes.get(1).getResultExpr());
			
			result.setContainsImplic(nodes.get(1).containsImplic());
			result.setContainsDisg(nodes.get(1).containsDisg());
			result.setContainsCong(nodes.get(1).containsCong());
			result.setContainsUnary(nodes.get(1).containsUnary());
			result.setContainsTerm(nodes.get(1).containsTerm());
		}
		if (_count==3)  // sono in una "(" + "fbf" + ")"
		{	
			
			if (argu.isInsideImplic()) // sto dentro una (co)impliicazione logica
			{
				// cancello sempre le parentesi, indpip. da cosa ho dentro la fbf
				// esempi: A => ( B <=> C ) 
				// oppure A => ( B or C) 
				// oppure A => ( B and C)
				// oppure  A => ( not B ) 
				// oppure A => ( B )
		
				result = nodes.get(1);
				
			}
			else if (argu.isInsideDisg()) // sto dentro una disgiunzione
			{
				if (nodes.get(1).containsImplic()) 
				{
					// lascio le parentesi solo se dentro la fbf ho una implicazione
					// esempi: A or ( B => C)		
					result.setResultExpr(nodes.get(0).getResultExpr() + nodes.get(1).getResultExpr() + nodes.get(2).getResultExpr());
						
				}
				else result = nodes.get(1);
				
				result.setContainsImplic(nodes.get(1).containsImplic());
				result.setContainsDisg(nodes.get(1).containsDisg());
				result.setContainsCong(nodes.get(1).containsCong());
				result.setContainsUnary(nodes.get(1).containsUnary());
				result.setContainsTerm(nodes.get(1).containsTerm());		
					
			}
			else if (argu.isInsideCong()) // sto dentro una congiunzione
			{
				if (nodes.get(1).containsImplic() || nodes.get(1).containsDisg() )
				{
					// lascio le parentesi solo se dentro la fbf ho una implicazione o una disgiunzione
					// esempi: A and ( B => C)
					// oppure A and ( B or C)		
					result.setResultExpr(nodes.get(0).getResultExpr() + nodes.get(1).getResultExpr() + nodes.get(2).getResultExpr());
						
				}
				else result = nodes.get(1);
				
				result.setContainsImplic(nodes.get(1).containsImplic());
				result.setContainsDisg(nodes.get(1).containsDisg());
				result.setContainsCong(nodes.get(1).containsCong());
				result.setContainsUnary(nodes.get(1).containsUnary());
				result.setContainsTerm(nodes.get(1).containsTerm());
			}
				
			else if (argu.isInsideUnaryOp()) // sto dentro un not o una quantificazione
			{
				if (nodes.get(1).containsImplic() || nodes.get(1).containsDisg() || nodes.get(1).containsCong() )
				{
					// lascio le parentesi solo se dentro la fbf ho una implicazione o una disgiunzione o una congiunzione
					// esempi: not ( A => B)
					// oppure not ( A or B )
					// oppure not ( A and B )
					result.setResultExpr(nodes.get(0).getResultExpr() + nodes.get(1).getResultExpr() + nodes.get(2).getResultExpr());
						
				}
				else result = nodes.get(1);
				
				result.setContainsImplic(nodes.get(1).containsImplic());
				result.setContainsDisg(nodes.get(1).containsDisg());
				result.setContainsCong(nodes.get(1).containsCong());
				result.setContainsUnary(nodes.get(1).containsUnary());
				result.setContainsTerm(nodes.get(1).containsTerm());
			}

			else if (argu.isInsideTerm()) // sto dentro un termine
			{
				// lascio le parentesi			
				result = nodes.get(1);
			}
			
		}
	
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"risultato: LISTA CON " + _count + " NODI " + result.toString() + ". argomento:" + argu.toString());
		if (debug) System.out.println(CompilerUtils.indent(level,8)+"VISIT(" + n.getClass().getName() + ") : FINE\n");
		this.level--;
		return result;
	}
	

}


